/*
 * Server.cpp
 *
 *  Created on: 2010-05-28
 *      Author: mateusz
 */

#include "Server.h"

#include "events.h"

//------------------------------------------------------------------------------

Server::Server() : nextUserId(1)
{
	slots.connect(network_server.sig_client_connected(), this, &Server::on_client_connected);
	slots.connect(network_server.sig_client_disconnected(), this, &Server::on_client_disconnected);
	slots.connect(network_server.sig_event_received(), this, &Server::on_event_received);
}

//------------------------------------------------------------------------------

Server::~Server()
{
}

//------------------------------------------------------------------------------

void Server::exec()
{
	network_server.start("6666");

	cl_log_event("system", "server started");

	while (true)
	{
		CL_Event::wait(network_server.get_event_arrived());
		network_server.process_events();
	}

	network_server.stop();
}

//------------------------------------------------------------------------------

void Server::on_client_connected(CL_NetGameConnection *connection)
{
	cl_log_event("network", "Client connected");

	ServerUser *user = new ServerUser();
	user->attachToConnection(connection);

	connected.push_back(user);

	sendToAll(CL_NetGameEvent(EVENT_PLAYER_CONNECTED,(int)(connected.size()-1)), user);

	user->sendEvent(CL_NetGameEvent(EVENT_PLAYER_ID,(int)(connected.size()-1)));
	user->sendEvent(CL_NetGameEvent(EVENT_MAP,(CL_String)map.getMap()));

	ServerPlayer* player = new ServerPlayer();
	players[connected.size()-1] = player;

	for( unsigned i = 0; i < connected.size() - 1; i++ ) {
		if( connected[i] != NULL && connected[i] != user ) {
			user->sendEvent(CL_NetGameEvent(EVENT_PLAYER_CONNECTED,(int)i));
			user->sendEvent(CL_NetGameEvent(EVENT_PLAYER_MOVE,(int)i,(int)players[i]->getX(),(int)players[i]->getY()));
			if(!players[i]->isAlive()) {
				user->sendEvent(CL_NetGameEvent(EVENT_PLAYER_DEAD,(int)i));
			}
		}
	}

	user->sendEvent(CL_NetGameEvent(EVENT_START_GAME));
}

//------------------------------------------------------------------------------

void Server::on_client_disconnected(CL_NetGameConnection *connection)
{
	cl_log_event("network", "Client disconnected");

	ServerUser *user = ServerUser::getUser(connection);

	unsigned i = 0;
	for( ; i < connected.size(); i++ ) {
		if( connected[i] == user )
			break;
	}

	sendToAll(CL_NetGameEvent(EVENT_PLAYER_DISCONNECTED,(int)i), user);

	connected[i] = NULL;
	players[i]->die();

	if(user)
		delete user;

	std::map<int,ServerPlayer*>::iterator iter = players.find(i);
	players.erase(iter);
}

//------------------------------------------------------------------------------

void Server::on_event_received(CL_NetGameConnection *connection, const CL_NetGameEvent &e)
{
	//cl_log_event("events", "Client sent event: %1", e.to_string());

	ServerUser *user = ServerUser::getUser(connection);
	if(user)
	{
		CL_String eventName = e.get_name();

		sendToAll( e, user );

		if( eventName == CL_String(EVENT_PLAYER_BOMB_PLANT) ) {
			handlePlayerBombPlant(e);
		} else if( eventName == CL_String(EVENT_PLAYER_MOVE) ) {
			handlePlayerMove(e);
		} else if( eventName == CL_String(EVENT_PLAYER_DEAD) ) {
			handleDead(e);
		}
	}
}

//------------------------------------------------------------------------------

void Server::sendToAll(const CL_NetGameEvent &event, const ServerUser *user) {
	for( unsigned i = 0; i < connected.size(); i++ ) {
		if( connected[i] != user && connected[i] != NULL )
			connected[i]->sendEvent(event);
	}
}

//------------------------------------------------------------------------------

void Server::handlePlayerBombPlant(const CL_NetGameEvent &event) {
	int playerId = event.get_argument(0);
	int x = event.get_argument(1);
	int y = event.get_argument(2);

	cl_log_event("event", "Player '%1' planted bomb at %2 %3", playerId, x, y);

	map.blow(x,y,3);
}

//------------------------------------------------------------------------------

void Server::handlePlayerMove(const CL_NetGameEvent &event) {
	int playerId = event.get_argument(0);
	int x = event.get_argument(1);
	int y = event.get_argument(2);

	cl_log_event("event", "Player '%1' move to %2 %3", playerId, x, y);

	players[playerId]->goTo(x,y);
}

//------------------------------------------------------------------------------

void Server::handleDead(const CL_NetGameEvent &event) {
	int playerId = event.get_argument(0);

	players[playerId]->die();

	cl_log_event("event", "Player '%1' died", playerId);

	if( players.size() == 1 ) {
		restartGame();
	} else {
		int numberOfAlives = 0;
		int aliveId;

		std::map<int,ServerPlayer*>::iterator iter = players.begin();
		for( unsigned i = 0; i < players.size(); i++ ) {
			if( (*iter).second->isAlive() ) {
				numberOfAlives++;
				aliveId = (*iter).first;
			}
			iter++;
		}
		CL_Console::write("alive: %1\n", numberOfAlives);
		if( numberOfAlives <= 1 ) {
			//wygral aliveId
			restartGame();
		}
	}
}

//------------------------------------------------------------------------------

void Server::restartGame() {
	map.generateMap();

	CL_System::sleep(2000);

	for( unsigned i = 0; i < connected.size(); i++ ) {
		if( connected[i] != NULL ) {
			connected[i]->sendEvent(CL_NetGameEvent(EVENT_MAP,(CL_String)map.getMap()));
			connected[i]->sendEvent(CL_NetGameEvent(EVENT_START_GAME));

			players[i]->resurrect();
		}
	}
}

//------------------------------------------------------------------------------
